// encoding utf-8

#ifndef __qyscilexer__
#define __qyscilexer__

#include <QMap>
#include <QVariant>
#include <QByteArray>
#include <Qsci/qscilexer.h>

/*! \class QySciLexer
	\brief Универсальный лексер.

	Лексер - это часть механизма подсветки синтаксиса в scintilla (подробнее см. ее описание).
	QySciLexer универсальный лексер, который можно сконфигурировать для подсветки практически любого типа файлов. При этом, следует учитывать что QySciLexer, используется лишь как конфигуратор, для заранее подготовленных шаблонов scintilla (полный список шаблонов см. в scintilla). Например для подсветки файлов языков с C-подобным синтаксисом (C++,JavaScript,Java,Squirrel и т.д.) может использоваться лексер для C++(шаблон называется "cpp").

	Приблизительно, список лексеров выглядит так: abaqus, ada, ans1, apdl, asm, asp, asy, au3, ave, baan, bash, batch, blitzbasic, bullant, caml, clw, clwnocase, cmake, conf, cpp, cppnocase, csound, css, d, diff, eiffel, eiffelkw, erlang, errorlist, escript, f77, flagship, forth, fortran, freebasic, gap, gui4cli, haskell, html, inno, kix, latex, lisp, lot, lout, lua, magiksf, make, matlab, metapost, mmixal, mssql, mysql, nncrontab, nsis, null, octave, opal, pascal, pb, perl, php, phpscript, plm, po, pov, powershell, progress, props, ps, purebasic, python, r, rebol, ruby, scriptol, smalltalk, specman, spice, sql, tads3, tcl, tex, vb, vbscript, verilog, vhdl, xml, yaml.

	QySciLexer конфигурируется в несколько этапов, первый этап это установка т.н. базовых параметров. Давайте рассмотрим конфигурацию лексера, на примере конфигурации лексера для C++.

	\code
	QySciLexer cppLexer; // это лексер, который мы будем конфигурировать

	cppLexer.setLanguage("C++"); // тут все просто, задаем имя для нашего лексера
	cppLexer.setLexer("cpp"); // это то про что я писал выше, имя шаблона
	cppLexer.setCaseSensitive(true); // чувствителен ли лексер к регистру символов
	cppLexer.setWordCharacters("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_#"); // символы которые может включать в себя "слово"
	cppLexer.setBlockStartString("{"); // откравающая опраторная скобка
	cppLexer.setBlockEndString("}"); // закрывающая опраторная скобка
	cppLexer.setBlockStartKeywordString("case catch class default do else finally for if private protected public struct try union while"); // список ключевых слов, после которых может начинаться блок

	\endcode

	Основа подготовлена, теперь нужно задать стили (набор стилей зависит от лексера - шаблона, см. scintilla). Под стилем понимается набор параметров (таких как цвет текса, цвет фона, шрифт и некоторые другие параметры), применяемые лексером к тем или иным участкам кода (текста файла). Стили задать можно только при помощи метода loadPropertyFromMap (это связано с тем, что данный лексер разрабатывался для вполне конкретных задачь, для которых данный интерфейс является наиболее приемлемым), который принимает набор: имя параметра - значение.
	Существует ряд ключевых параметров, значения которых будут влиять на имена других ситываемых параметров. Примером такого значения является параметр "styles", в котором перечисляются названия стилей, названия стилей могут быть любыми, важно лишь чтобы их номера (идентификаторы) соответствовали номарам указанным в лексере - шаблоне. Именно по идентификатору стиля, лексер получает параметры стиля, имяена используются для удобства. Зачастую стили в шиблоне имеют номерацию по порядку. Если пользователь не указывает идентификаторы, то идентификаторы выдаются автоматически, идентификатором стиля назначается его порядковый номер (начинается с 0) в списке стилей.

	\code

	QMap<QString,QVariant> propertyMap; // сюда мы будем писать

	// список стилей для cpp
	propertyMap["styles"] =
		"Default, Comment, CommentLine, CommentDoc, Number, Keywords, DoubleQuotedString, "
		"SingleQuotedString, UUID, PreProcessor, Operators, Identifier, UnclosedString, VerbatimString, "
		"Regex, CommentLineDoc, KeywordSet2, CommentDocKeyword, CommentDocKeywordError, GlobalClass";

	// Цвета и шрифт по умолчанию, если вы для стиля не задали явно цвет для фона, цвет символов или шрифт, то используется значение по умолчанию.
	propertyMap["defaultColor"] = "#000000";
	propertyMap["defaultPaper"] = "#ffffff";
	propertyMap["defaultFont"] = "terminus,12,-1,5,50,0,0,0,0,0";
	propertyMap["defaultEolFill"] = 0; // EolFill это флаг, если его значение 1, то строка заливается установленым цветом фона до конца, иначе, заливается только фон за символами.

	// я не буду расписывать каждый стиль, приведу лишь несколько

	propertyMap["Comment.fore"] = "#cccccc"; // как можно заметить, имя параметра состоит из имени стиля (Comment), это то о чем я говорил выше
	propertyMap["Comment.id"] = 1; // указываем идентификатор
	propertyMap["PreProcessor.fore"] = "#ffffff"; // цвет текста
	propertyMap["PreProcessor.back"] = "#000000"; // цвет фона
	propertyMap["PreProcessor.eolFill"] = 1; // применить фон до конца строки

	// и последний немаловажный параметр - ключевые слова. Каждый лексер, позволяет определять разное кол-во наборов ключевых слов. Например cpp лексер позволяет определить 3 набора
	propertyMap["keywords1"] = "and and_eq asm auto bitand bitor bool break case catch char class compl const const_cast continue default delete do double dynamic_cast else enum explicit export extern false float for friend goto if inline int long mutable namespace new not not_eq operator or or_eq private protected public register reinterpret_cast return short signed sizeof static static_cast struct switch template this throw true try typedef typeid typename union unsigned using virtual void volatile wchar_t while xor xor_eq"; // операторы, модификаторы и т.п.
	propertyMap["keywords2"] = ""; // для подсветки пользовательских типов, я например здесь перечисляю типы Qt =)
	propertyMap["keywords3"]  = "a addindex addtogroup anchor arg attention author b brief bug c class code date def defgroup deprecated dontinclude e em endcode endhtmlonly endif endlatexonly endlink endverbatim enum example exception f$ f[ f] file fn hideinitializer htmlinclude htmlonly if image include ingroup internal invariant interface latexonly li line link mainpage name namespace nosubgrouping note overload p page par param post pre ref relates remarks return retval sa section see showinitializer since skip skipline struct subsection test throw todo typedef union until var verbatim verbinclude version warning weakgroup $ @ \\ & < > # { }"; // это для подсветки ключевых слов в комментариях, данный набор для doxygen

	\endcode

*/

class QySciLexer : public QsciLexer {
    Q_OBJECT
	Q_PROPERTY( QString language READ languageQStr WRITE setLanguage )
	Q_PROPERTY( QString lexer READ lexerQStr WRITE setLexer )
	Q_PROPERTY( QStringList autoCompletionWordSeparators READ autoCompletionWordSeparators WRITE setAutoCompletionWordSeparators )
	Q_PROPERTY( int blockLookback READ blockLookback WRITE setBlockLookback )
	Q_PROPERTY( int braceStyle READ braceStyle WRITE setBraceStyle )
	Q_PROPERTY( bool caseSensitive READ caseSensitive WRITE setCaseSensitive )
	Q_PROPERTY( int defaultStyle READ defaultStyle WRITE setDefaultStyle )
	Q_PROPERTY( QString wordCharacters READ wordCharactersQStr WRITE setWordCharacters )
	Q_PROPERTY( int blockEndStyle READ blockEndStyle WRITE setBlockEndStyle )
	Q_PROPERTY( int blockStartStyle READ blockStartStyle WRITE setBlockStartStyle )
	Q_PROPERTY( int blockStartKeywordStyle READ blockStartKeywordStyle WRITE setBlockStartKeywordStyle )
	Q_PROPERTY( QString blockEndString READ blockEndString WRITE setBlockEndString )
	Q_PROPERTY( QString blockStartString READ blockStartString WRITE setBlockStartString )
	Q_PROPERTY( QString blockStartKeywordString READ blockStartKeywordString WRITE setBlockStartKeywordString )
	Q_PROPERTY( QString defaultColor READ defaultColorQStr WRITE setDefaultColor )
	Q_PROPERTY( QString defaultPaper READ defaultPaperQStr WRITE setDefaultPaper )
	Q_PROPERTY( QString defaultFont READ defaultFontQStr WRITE setDefaultFont )
	Q_PROPERTY( bool defaultEolFill READ defaultEolFill WRITE setDefaultEolFill )
	Q_PROPERTY( int autoIndentStyle READ autoIndentStyle WRITE setAutoIndentStyle )
	Q_PROPERTY( QString lineComment READ lineComment WRITE setLineComment )

public:
	using QsciLexer::setDefaultColor;
	using QsciLexer::setDefaultPaper;
	using QsciLexer::setDefaultFont;

    QySciLexer( QObject * parent=0 ); //!< Конструктор
    virtual ~QySciLexer(); //!< Диструктор

    const char * language() const; //!< имя
    const char * lexer() const; //!< лексер - шаблон
    QString languageQStr() const { return language(); } //!< имя
    QString lexerQStr() const { return lexer(); } //!< лексер - шаблон
    QStringList autoCompletionWordSeparators() const; //!< список разделителей для автозавершения (для С++ например, это "::", "->" и "." )
	const char * blockEnd( int * style=0 ) const; //!< конец блока, где style - стиль применяемый для подсветки
    int blockLookback() const; // ? (см. scintilla/qscintilla)
    const char * blockStart( int * style=0 ) const; //!< начало блока, где style - стиль применяемый для подсветки
    const char * blockStartKeyword( int * style=0 ) const; //!< список ключевых слов, после которых может начинаться блок, где style - стиль применяемый для подсветки
    int braceStyle() const; //!< стиль для скобок
    bool caseSensitive() const; //!< чувствительность к регистру
    const char * keywords( int set ) const; //!< получить набор ключевых слов
    int defaultStyle() const; //!< стиль по умолчанию
    QString description( int style ) const; //!< описание для стиля
    const char * wordCharacters() const { return p_wordCharacters.constData(); } //!< символы которые может включать в себя "слово"
    QString wordCharactersQStr() const { return p_wordCharacters; } //!< символы которые может включать в себя "слово"

	int blockEndStyle() const { return p_blockEndStyle; } //!< стиль для конца блока
	int blockStartStyle() const { return p_blockStartStyle; } //!< стиль для начала блока
	int blockStartKeywordStyle() const { return p_blockStartKeywordStyle; } //!< стиль для ключевых слов, после которых может начинаться блок
	QString blockEndString() const { return p_blockEnd; } //!< конец блока
	QString blockStartString() const { return p_blockStart; } //!< начало блока
	QString blockStartKeywordString() const { return p_blockStartKeyword; } //!< список ключевых слов, после которых может начинаться блок
	bool defaultEolFill() const { return p_defaultEolFill; } //!< EolFill по умолчанию

	QString defaultColorQStr() const { return defaultColor().name(); } //!< цвет текста по умолчанию
	QString defaultPaperQStr() const { return defaultPaper().name(); } //!< цвет фона по умолчанию
	QString defaultFontQStr() const { return defaultFont().toString(); } //!< шрифт по умолчанию

	QString lineComment() const { return p_lineComment; } //!< символ(ы) комментария

	Q_INVOKABLE QVariant lexerProperty( const QString & name ) const; //!< свойство лексера

public slots:
	void setLanguage( const QString & language ); //!< установить имя
	void setLexer( const QString & lexer ); //!< установить имя лексера - шаблона
	void setAutoCompletionWordSeparators( QStringList autoCompletionWordSeparators ); //!< установить список разделителей для автозавершения
	void setBlockLookback( int blockLookback ) { p_blockLookback = blockLookback; } // ? (см. scintilla/qscintilla)
	void setBraceStyle( int style ) { p_braceStyle = style; } //!< установить стиль для конца блока
	void setDefaultStyle( int style ) { p_defaultStyle = style; } //!< установить стиль по умолчанию
	void setCaseSensitive( bool caseSensitive ) { p_caseSensitive = caseSensitive; } //!< установить чувствительность к регистру
    void setWordCharacters( const QString & wordCharacters ) { p_wordCharacters = wordCharacters.toAscii(); } //!< установить символы которые может включать в себя "слово"
	void setBlockEndStyle( int style ) { p_blockEndStyle = style; } //!< установить стиль для конца блока
	void setBlockStartStyle( int style ) { p_blockStartStyle = style; } //!< установить стиль для начала блока
	void setBlockStartKeywordStyle( int style ) { p_blockStartKeywordStyle = style; } //!< установить стиль для слов, после которых может начинаться блок
	void setBlockEndString( const QString & str ) { p_blockEnd = str.toAscii(); } //!< установить конец блока
	void setBlockStartString( const QString & str ) { p_blockStart = str.toAscii(); } //!< установить начало блока
	void setBlockStartKeywordString( const QString & str ) { p_blockStartKeyword = str.toAscii(); } //!< установить список ключевых слов, после которых может начинаться блок
	void setDefaultEolFill( bool defaultEolFill ) { p_defaultEolFill = defaultEolFill; } //!< установить EolFill по умолчанию
	void setDefaultColor( const QString & str ) { setDefaultColor( QColor(str) ); } //!< установить цвет текста по умолчанию
	void setDefaultPaper( const QString & str ) { setDefaultPaper( QColor(str) ); } //!< установить цвет фона по усолчанию
	void setDefaultFont( const QString & str ) { QFont f; f.fromString(str); setDefaultFont(f); } //!< установить шрифт по умолчанию
	void setLexerProperty( const QString & name, const QVariant & value ); //!< установить свойство лексера - шаблона
	void clearSettings(); //!< очистить списки ключевых слов, свойств лексера и т.п.
	void setLineComment( const QString & lineComment ) { p_lineComment = lineComment.toAscii(); } //!< установить символ(ы) комментария

	void loadPropertyFromMap( QMap<QString,QVariant> propertyMap ); //!< загрузить настройки из propertyMap
	void refreshProperties(); //!< обновить свойства лексера

private:
	void init();

	QByteArray p_language, p_lexer,
		p_blockStart, p_blockEnd, p_blockStartKeyword,
		p_wordCharacters,
		p_lineComment;
	QStringList p_autoCompletionWordSeparators;
	int p_blockLookback,
		p_blockStartStyle, p_blockEndStyle, p_blockStartKeywordStyle,
		p_braceStyle,
		p_defaultStyle;
	bool p_caseSensitive, p_defaultEolFill;
	QMap<int,QByteArray> keywordsSets,
		styleDescriptions;
	QMap<QString,QVariant> p_lexerProperties;

};

#endif/*__qyscilexer__*/
